package com.hp.bon.sgw.service;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.hp.bon.sgw.domain.BlackListLog;
import com.hp.bon.sgw.domain.BusinessRecord;
import com.hp.bon.sgw.domain.MessageDetail;
import com.hp.bon.sgw.domain.ProcessLog;
import com.hp.bon.sgw.domain.Top100KeyWordBean;
import com.hp.bon.sgw.util.Comutils;

public class LoadJsonDataService extends Thread {
	private static final Logger logger = LoggerFactory.getLogger(LoadJsonDataService.class);

	private static LoadJsonDataService _instance;
	private boolean isSleeping = false;
	private boolean exit = false;
	private int loop_interval = 300;

	public static LoadJsonDataService getInstance() {
		if (_instance == null) {
			_instance = new LoadJsonDataService();
		}
		return _instance;
	}

	public void exit() {
		exit = true;
	}

	public void kickOff() {
		if (isSleeping) {
			interrupt();
		}
	}

	private Class<?>[] jsonDomainClass = { ProcessLog.class, BusinessRecord.class, MessageDetail.class, Top100KeyWordBean.class, BlackListLog.class };
	private OuterDBDataService outDataStoreService;
	private String dataPath;
	private String backupPath;
	private ClassPathXmlApplicationContext ctx = null;

	public void run() {
		logger.info("LoadJsonDataService is running ..");
		dataPath = System.getProperty("json_data_path");
		backupPath = System.getProperty("backupPath");
		loop_interval = Integer.valueOf(System.getProperty("loop_interval", "300"));
		logger.info("json_data_path is " + dataPath);
		logger.info("backupPath is " + backupPath);
		logger.info("loop_interval is " + loop_interval);
		String[] springXmls = new String[] { "spring-base.xml" };
		try {
			ctx = new ClassPathXmlApplicationContext(springXmls);
			outDataStoreService = ctx.getBean(OuterDBDataService.class);
			while (!exit) {
				doJob();
				try {
					isSleeping = true;
					Thread.sleep(loop_interval * 1000);
					isSleeping = false;
				} catch (InterruptedException e) {
					isSleeping = false;
					logger.warn("thread interrupted ." + e.fillInStackTrace());
				}
			}
			logger.info("exit LoadDataThreader.");
		} catch (Exception e) {
			logger.warn(e.toString());
		}
	}

	@SuppressWarnings("rawtypes")
	public void doJob() {
		logger.info(">>>>>> do db save job(json)");
		File fileDir = new File(this.dataPath);
		if (fileDir.exists() && fileDir.isDirectory() && fileDir.listFiles() != null) {
			File[] files = fileDir.listFiles();

			// 按文件最后修改时间进行排序
			Arrays.sort(files, new CompratorByLastModified());

			for (int i = 0; i < files.length; i++) {
				Class matchedCls = canPublishToDB(files[i]);
				if (matchedCls != null) {
					logger.info("process data file " + files[i].getName() + " ......");
					String newFileName = files[i].getAbsolutePath() + ".working";
					File newFile = new File(newFileName);
					boolean success = files[i].renameTo(newFile);
					if (success) {
						logger.info("rename " + files[i].getName() + " to " + newFile.getName());
						try {
							long start = System.currentTimeMillis();
							jsonDataToDB(newFile, matchedCls);
							long used = System.currentTimeMillis() - start;
							if (used >= 5 * 60 * 1000) {
								logger.warn("save " + files[i].getName() + " to db end,used time ms=" + used + ",saved too slow.");
							} else {
								logger.info("save " + files[i].getName() + " to db end,used time ms= " + used);
							}
							moveJsonFile(newFile, files[i].getName(), matchedCls.getSimpleName());
						} catch (Exception e) {
							logger.warn("jsonDataToDB err:" + e);
						}
					} else {
						logger.info("rename " + files[i].getName() + " failed.");
					}
				} else {
					// logger.info(file.getName() +
					// " not need save to db in this period");
				}
			}
		} else {
			logger.info("not found any files under " + this.dataPath);
		}

	}

	/**
	 * 每15分钟一个文件，当某种数据的文件的最后一次访问早于系统当前15分钟以上时，说明这个数据文件已经不在会 有新数据写入，可以处理了。
	 */

	@SuppressWarnings("rawtypes")
	private Class canPublishToDB(File theFile) {
		String fileName = theFile.getName();
		boolean canProcess = false;
		if (fileName.endsWith(".json") && theFile.isFile()) {
			try {
				String curfileTime = Comutils.datafileFormat();
				String fileTime = fileName.substring(fileName.lastIndexOf("_") + 1, fileName.lastIndexOf("."));

				// 文件名时间非当前文件名时间，并且，文件已经有1分钟没有进行写入
				canProcess = fileTime.compareToIgnoreCase(curfileTime) < 0 && theFile.lastModified() < System.currentTimeMillis() - 1 * 60 * 1000;
			} catch (Exception e) {
				logger.warn(e.toString());
			}
		}
		if (!canProcess) {
			return null;
		}
		for (Class cls : jsonDomainClass) {
			if (fileName.contains(cls.getSimpleName())) {
				return cls;
			}
		}
		return null;
	}

	private void moveJsonFile(File theFile, String fileName, String className) {
		String datePath = Comutils.formatTime(theFile.lastModified(), "yyyyMMdd");
		File destPath = new File(backupPath, datePath + File.separator + className);
		logger.info("prepare backup " + theFile.getName() + "... ");
		if (!destPath.exists()) {
			if (!destPath.mkdirs()) {
				logger.warn("create json file backup dir err:" + destPath.getPath());
			}
		}
		boolean suc = theFile.renameTo(new File(destPath, fileName));
		if (!suc) {
			logger.warn("backup file to backup dir err:" + theFile.getName());
			theFile.renameTo(new File(theFile.getParentFile(), theFile.getName() + ".bak"));
		} else {
			logger.info("backup " + theFile.getName() + " to " + destPath.getAbsolutePath() + File.separator + fileName);
		}
		logger.info("process end.");
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void jsonDataToDB(File file, Class jsonClass) throws Exception {
		Gson gson = new GsonBuilder().setDateFormat("yyyyMMddHHmmss.SSS").create();
		LineNumberReader reader = null;
		int savedTotal = 0;
		ArrayList<Object> domainList = new ArrayList<Object>(1000);
		try {
			reader = new LineNumberReader(new InputStreamReader(new BufferedInputStream(new FileInputStream(file)), "utf-8"));
			String line = null;
			while ((line = reader.readLine()) != null) {
				Object domainObj = gson.fromJson(line, jsonClass);
				domainList.add(domainObj);
				if (domainList.size() == 2000) {
					outDataStoreService.bulkSaveToDB(domainList);
					savedTotal += domainList.size();
					domainList.clear();
				}
			}
			// 处理最后一批
			if (!domainList.isEmpty()) {
				outDataStoreService.bulkSaveToDB(domainList);
				savedTotal += domainList.size();
				domainList.clear();
			}
			domainList = null;
			logger.info("saved " + savedTotal + " " + jsonClass.getSimpleName() + " records");
		} catch (Exception e) {
			logger.warn("saved " + savedTotal + " records,exception:" + e);
		} finally {
			if (reader != null) {
				reader.close();
			}
		}
	}

	static class CompratorByLastModified implements Comparator<File> {
		public int compare(File f1, File f2) {
			long diff = f1.lastModified() - f2.lastModified();
			if (diff > 0)
				return 1;
			else if (diff == 0)
				return 0;
			else
				return -1;
		}

		public boolean equals(Object obj) {
			return true;
		}
	}

}